/*
 *
 *  * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 *
 *
 */

package com.iwindplus.base.util;

import cn.hutool.core.map.MapUtil;
import com.iwindplus.base.domain.constant.CommonConstant;
import com.iwindplus.base.domain.constant.CommonConstant.SymbolConstant;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import lombok.extern.slf4j.Slf4j;

/**
 * Micrometer Observation 快捷工具类 提供“拿注册表 → 创建 Observation → 加标签 → 记录”一站式 API， 同时支持 Spring 环境与纯单元测试场景.
 *
 * @author zengdegui
 * @since 2021/1/11
 */
@Slf4j
public class ObservationUtil {

    private static final AtomicBoolean BUILD_FLAG = new AtomicBoolean(false);

    private static final AtomicReference<ObservationRegistry> OBSERVATION_REGISTRY = new AtomicReference<>();

    private ObservationUtil() {
        throw new IllegalStateException(CommonConstant.UTILITY_CLASS);
    }

    /**
     * 获取ObservationRegistry.
     *
     * @return ObservationRegistry
     */
    public static ObservationRegistry getObservationRegistry() {
        return OBSERVATION_REGISTRY.updateAndGet(registry -> {
            if (registry != null) {
                return registry;
            }

            return ObservationRegistry.NOOP;
        });
    }

    /**
     * 设置 setObservationRegistry.
     *
     * @param observationRegistry observationRegistry
     */
    public static void setObservationRegistry(ObservationRegistry observationRegistry) {
        if (BUILD_FLAG.compareAndSet(false, true)) {
            OBSERVATION_REGISTRY.set(observationRegistry);
            log.info("ObservationRegistry build success");
            return;
        }

        log.warn("ObservationRegistry already built, later config ignored");
    }

    /**
     * 根据名称 + Map 标签创建 Observation.
     *
     * @param name    指标名称，如 "user.save"
     * @param lowTags 低基数标签（可 null）
     * @return Observation
     */
    public static Observation of(String name, Map<String, String> lowTags) {
        Observation observation = Observation.createNotStarted(name, getObservationRegistry());
        if (MapUtil.isNotEmpty(lowTags)) {
            lowTags.forEach(observation::lowCardinalityKeyValue);
        }
        return observation;
    }

    /**
     * 根据名称 + 可变 key-value 字符串创建 Observation. 示例：ObservationUtil.of("order.create", "source=web", "channel=app")
     *
     * @param name 指标名称
     * @param kvs  成对的 key-value，必须偶数个
     * @return Observation
     */
    public static Observation of(String name, String... kvs) {
        Observation observation = Observation.createNotStarted(name, getObservationRegistry());
        Optional.ofNullable(kvs)
            .ifPresent(tags -> Arrays.stream(tags)
                .map(t -> t.split(SymbolConstant.EQUAL, 2))
                .filter(kv -> kv.length == 2)
                .forEach(kv -> observation.lowCardinalityKeyValue(kv[0], kv[1])));
        return observation;
    }

    /**
     * 执行 CheckedRunnable：自动完成 start → 业务逻辑 → stop / error. 异常会被原样抛出，方便 Spring 事务与全局异常处理器生效.
     *
     * @param name     指标名称
     * @param lowTags  低基数标签（可 null）
     * @param runnable 业务代码块
     */
    public static void run(String name, Map<String, String> lowTags, Observation.CheckedRunnable runnable) {
        of(name, lowTags).observeChecked(runnable);
    }

    /**
     * 执行 CheckedCallable：自动完成 start → 业务逻辑 → stop / error， 并返回业务结果.
     *
     * @param name     指标名称
     * @param lowTags  低基数标签（可 null）
     * @param callable 业务代码块
     * @param <T>      返回值类型
     * @return <T, S extends Exception> T
     * @throws Exception
     */
    public static <T, S extends Exception> T call(String name, Map<String, String> lowTags,
        Observation.CheckedCallable<T, S> callable) throws Exception {
        return of(name, lowTags).observeChecked(callable);
    }

    /**
     * 传统模式：先 start，再手动 stop. 适合需要多次埋点或复杂分支场景.
     *
     * @param name    指标名称
     * @param lowTags 低基数标签（可 null）
     * @return Observation
     */
    public static Observation start(String name, Map<String, String> lowTags) {
        return of(name, lowTags).start();
    }

}
